using System;
using System.Collections.Generic;
using System.Text;

namespace Dare.Utilities.Data
{
    public sealed class DbConvert
    {
        private static readonly Type NullableGenericTypeDefinition = typeof(Nullable<int>).GetGenericTypeDefinition();

        public static T ChangeType<T>(object obj, T defValIfNull)
        {
            if (obj == null || obj == DBNull.Value)
            {
                return defValIfNull;
            }

            Type targetType = typeof(T);

            if (targetType.IsInstanceOfType(obj))
            {
                return (T)obj;
            }
            else if (targetType.IsGenericType && targetType.GetGenericTypeDefinition() == NullableGenericTypeDefinition)
            {
                targetType = Nullable.GetUnderlyingType(targetType);
            }

            if (targetType.IsEnum && obj is string)
            {
                return (T)Enum.Parse(targetType, (string)obj);
            }
            else if (obj is IConvertible && typeof(IConvertible).IsAssignableFrom(targetType))
            {
                return (T)Convert.ChangeType(obj, targetType);
            }

            return (T)obj;
        }

        public static T ChangeTypeSafe<T>(object obj, T defVal)
        {
            try
            {
                return ChangeType<T>(obj, defVal);
            }
            catch
            {
                return defVal;
            }
        }

        public static int? ToNullableInt32(object obj)
        {
            if (obj is int)
            {
                return (int)obj;
            }
            else if (obj is string)
            {
                int num;
                if (int.TryParse((string)obj, out num))
                {
                    return num;
                }
                else
                {
                    return null;
                }
            }
            else if (obj is IConvertible)
            {
                try
                {
                    return Convert.ToInt32(obj);
                }
                catch
                {
                    return null;
                }
            }
            else
            {
                return null;
            }
        }

        public static int? ToNullableInt32(string obj)
        {
            int num;
            if (int.TryParse(obj, out num))
            {
                return num;
            }
            else
            {
                return null;
            }
        }

        public static uint? ToNullableUInt32(object obj)
        {
            if (obj is uint)
            {
                return (uint)obj;
            }
            else if (obj is string)
            {
                uint num;
                if (uint.TryParse((string)obj, out num))
                {
                    return num;
                }
                else
                {
                    return null;
                }
            }
            else if (obj is IConvertible)
            {
                try
                {
                    return Convert.ToUInt32(obj);
                }
                catch
                {
                    return null;
                }
            }
            else
            {
                return null;
            }
        }

        public static uint? ToNullableUInt32(string obj)
        {
            uint num;
            if (uint.TryParse(obj, out num))
            {
                return num;
            }
            else
            {
                return null;
            }
        }

        public static ushort? ToNullableUInt16(object obj)
        {
            if (obj is ushort)
            {
                return (ushort)obj;
            }
            else if (obj is string)
            {
                ushort num;
                if (ushort.TryParse((string)obj, out num))
                {
                    return num;
                }
                else
                {
                    return null;
                }
            }
            else if (obj is IConvertible)
            {
                try
                {
                    return Convert.ToUInt16(obj);
                }
                catch
                {
                    return null;
                }
            }
            else
            {
                return null;
            }
        }

        public static long? ToNullableInt64(object obj)
        {
            if (obj is long)
            {
                return (long)obj;
            }
            else if (obj is string)
            {
                long num;
                if (long.TryParse((string)obj, out num))
                {
                    return num;
                }
                else
                {
                    return null;
                }
            }
            else if (obj is IConvertible)
            {
                try
                {
                    return Convert.ToInt64(obj);
                }
                catch
                {
                    return null;
                }
            }
            else
            {
                return null;
            }
        }

        public static decimal? ToNullableDecimal(object obj)
        {
            if (obj is decimal)
            {
                return (decimal)obj;
            }
            else if (obj is string)
            {
                decimal num;
                if (decimal.TryParse((string)obj, out num))
                {
                    return num;
                }
                else
                {
                    return null;
                }
            }
            else if (obj is IConvertible)
            {
                try
                {
                    return Convert.ToDecimal(obj);
                }
                catch
                {
                    return null;
                }
            }
            else
            {
                return null;
            }
        }

        public static bool? ToNullableBoolean(object obj)
        {
            if (obj is bool)
            {
                return (bool)obj;
            }
            else if (obj is int)
            {
                return (int)obj != 0;
            }
            else if (obj is string)
            {
                bool result;
                int num;
                if (bool.TryParse((string)obj, out result))
                {
                    return result;
                }
                else if (int.TryParse((string)obj, out num))
                {
                    return num != 0;
                }
                else
                {
                    return null;
                }
            }
            else if (obj is IConvertible)
            {
                try
                {
                    return Convert.ToBoolean(obj);
                }
                catch
                {
                    return null;
                }
            }
            else
            {
                return null;
            }
        }

        public static double? ToNullableDouble(object obj)
        {
            if (obj is double)
            {
                return (double)obj;
            }
            else if (obj is string)
            {
                double num;
                if (double.TryParse((string)obj, out num))
                {
                    return num;
                }
                else
                {
                    return null;
                }
            }
            else if (obj is IConvertible)
            {
                try
                {
                    return Convert.ToDouble(obj);
                }
                catch
                {
                    return null;
                }
            }
            else
            {
                return null;
            }
        }

        public static DateTime? ToNullableDateTime(object obj, params string[] formats)
        {
            if (obj is DateTime)
            {
                return (DateTime)obj;
            }
            else if (obj is string)
            {
                DateTime val;

                if (formats.Length > 0)
                {
                    if (DateTime.TryParseExact((string)obj, formats, null, System.Globalization.DateTimeStyles.None, out val))
                    {
                        return val;
                    }
                    else
                    {
                        return null;
                    }
                }
                else
                {
                    if (DateTime.TryParse((string)obj, out val))
                    {
                        return val;
                    }
                    else
                    {
                        return null;
                    }
                }
            }
            else if (obj is IConvertible)
            {
                try
                {
                    return Convert.ToDateTime(obj);
                }
                catch
                {
                    return null;
                }
            }
            else
            {
                return null;
            }
        }

        public static Guid? ToNullableGuid(object obj)
        {
            if (obj is Guid)
            {
                return (Guid)obj;
            }
            else if (obj is string)
            {
                try
                {
                    if ((string)obj == String.Empty) return null;
                    return new Guid((string)obj);
                }
                catch
                {
                    return null;
                }
            }
            else
            {
                return null;
            }
        }
    }
}
